// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package x509

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"time"
	"unicode/utf8"
)

type InvalidReason int

const (
	// 当证书由另一个未标记为CA证书的
	// 签名时，NotAuthorizedToSign结果。
	NotAuthorizedToSign InvalidReason = iota
	// 证书过期时的过期结果，基于VerifyOptions中给出的时间
	// 计算。
	Expired
	// 当中间或根
	// 证书具有不允许在叶证书中使用DNS或
	// 其他名称（包括IP地址）的名称约束时，会产生CANOTAutAuthorizedForthisname。
	CANotAuthorizedForThisName
	// Tooman在违反路径长度约束
	// 。
	TooManyIntermediates
	// 当证书的密钥用法表明
	// 它只能用于其他目的时，会产生不兼容。
	IncompatibleUsage
	// 当父证书的使用者名称
	// 与子证书中的颁发者名称不匹配时，会产生名称不匹配的结果。
	NameMismatch
	// NameConstraintsWithoutSANs是一个遗留错误，不再返回。
	NameConstraintsWithoutSANs
	// 当CA证书包含允许的
	// 名称约束，但叶证书包含
	// 不支持或不受约束类型的名称时，会产生UnconstrainedName。
	UnconstrainedName
	// 当检查证书所需的比较操作数
	// 超过
	// VerifyOptions设置的限制时，TooManyConstraints结果。最大约束比较。此限制适用于
	// 防止病理证书可能会消耗过多的
	// CPU时间进行验证。
	TooManyConstraints
	// 当中间或根
	// 证书不允许请求的扩展密钥使用时，CANOTAutAuthorizedForExtKeyUsage结果。
	CANotAuthorizedForExtKeyUsage
)

// CertificateInvalidError在出现奇数错误时产生。
// 库的用户可能希望统一处理所有这些错误。
type CertificateInvalidError struct {
	Cert   *Certificate
	Reason InvalidReason
	Detail string
}

func (e CertificateInvalidError) Error() string {
	switch e.Reason {
	case NotAuthorizedToSign:
		return "x509: certificate is not authorized to sign other certificates"
	case Expired:
		return "x509: certificate has expired or is not yet valid: " + e.Detail
	case CANotAuthorizedForThisName:
		return "x509: a root or intermediate certificate is not authorized to sign for this name: " + e.Detail
	case CANotAuthorizedForExtKeyUsage:
		return "x509: a root or intermediate certificate is not authorized for an extended key usage: " + e.Detail
	case TooManyIntermediates:
		return "x509: too many intermediates for path length constraint"
	case IncompatibleUsage:
		return "x509: certificate specifies an incompatible key usage"
	case NameMismatch:
		return "x509: issuer name does not match subject from issuing certificate"
	case NameConstraintsWithoutSANs:
		return "x509: issuer has name constraints but leaf doesn't have a SAN extension"
	case UnconstrainedName:
		return "x509: issuer has name constraints but leaf contains unknown or unconstrained name: " + e.Detail
	}
	return "x509: unknown error"
}

// 当授权名称集与
// 请求的名称不匹配时，会出现HostName错误。
type HostnameError struct {
	Certificate *Certificate
	Host        string
}

func (h HostnameError) Error() string {
	c := h.Certificate

	if !c.hasSANExtension() && matchHostnames(c.Subject.CommonName, h.Host) {
		return "x509: certificate relies on legacy Common Name field, use SANs instead"
	}

	var valid string
	if ip := net.ParseIP(h.Host); ip != nil {
		// 尝试验证IP 
		if len(c.IPAddresses) == 0 {
			return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
		}
		for _, san := range c.IPAddresses {
			if len(valid) > 0 {
				valid += ", "
			}
			valid += san.String()
		}
	} else {
		valid = strings.Join(c.DNSNames, ", ")
	}

	if len(valid) == 0 {
		return "x509: certificate is not valid for any names, but wanted to match " + h.Host
	}
	return "x509: certificate is valid for " + valid + ", not " + h.Host
}

// 当证书颁发者未知时，UnknownAuthority错误结果
type UnknownAuthorityError struct {
	Cert *Certificate
	// 提示包含一个错误，该错误可能有助于确定为什么找不到
	// 颁发机构。
	hintErr error
	// hintCert包含一个可能的权威证书，该证书由于hintErr中的错误而被拒绝。
	hintCert *Certificate
}

func (e UnknownAuthorityError) Error() string {
	s := "x509: certificate signed by unknown authority"
	if e.hintErr != nil {
		certName := e.hintCert.Subject.CommonName
		if len(certName) == 0 {
			if len(e.hintCert.Subject.Organization) > 0 {
				certName = e.hintCert.Subject.Organization[0]
			} else {
				certName = "serial:" + e.hintCert.SerialNumber.String()
			}
		}
		s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
	}
	return s
}

// SystemRootsError无法加载系统根证书时的结果。
type SystemRootsError struct {
	Err error
}

func (se SystemRootsError) Error() string {
	msg := "x509: failed to load system roots and no roots provided"
	if se.Err != nil {
		return msg + "; " + se.Err.Error()
	}
	return msg
}

func (se SystemRootsError) Unwrap() error { return se.Err }

// 当证书没有ASN时，返回errNotParsed。1内容为
// 已验证。特定于平台的验证需要ASN。1.内容。
var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificate")

// VerifyOptions包含证书的参数。验证
type VerifyOptions struct {
	// DNSName，如果设置了，将根据带有
	// certificate的叶证书进行检查。验证主机名或平台验证程序。
	DNSName string

	// 中间人是一个可选的证书池，它不是信任的
	// 锚点，但可用于形成从叶证书到
	// 根证书的链。
	Intermediates *CertPool
	// 根是叶证书需要链接到的一组受信任的根证书。如果为nil，则使用系统根或平台验证器。
	Roots *CertPool

	// CurrentTime用于检查
	// 链中所有证书的有效性。如果为零，则使用当前时间。
	CurrentTime time.Time

	// KeyUsages指定哪些扩展密钥用法值是可接受的。
	// 如果允许列出的任何值，则接受该链。空列表
	// 表示ExtKeyUsageServerAuth。要接受任何密钥用法，请包括ExtKeyUsageAny。
	KeyUsages []ExtKeyUsage

	// MaxConstraintComparisons是检查给定证书的名称约束时与
	// 执行的最大比较次数。如果
	// 为零，则使用合理的默认值。此限制可防止病理性
	// 证书在
	// 验证时占用过多的CPU时间。它不适用于平台验证程序。
	MaxConstraintComparisions int
}

const (
	leafCertificate = iota
	intermediateCertificate
	rootCertificate
)

// RFC2821邮箱通过将其分为“本地”（即“@”之前）和“域”
// 用户的电子邮件地址）。
// 两部分来表示“邮箱”（这是大多数
type rfc2821Mailbox struct {
	local, domain string
}

// parserfc2821邮箱将电子邮件地址解析为本地和域部分，
// 基于RFC 2821中“邮箱”的ABNF。根据RFC 5280，
// 第4.2.1.6节，这对于证书中的RFC822名称是正确的：“
// RFC822名称的格式是RFC 2821第4.1.2节中定义的“邮箱”。
func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
	if len(in) == 0 {
		return mailbox, false
	}

	localPartBytes := make([]byte, 0, len(in)/2)

	if in[0] == '"' {
		// Quoted string=DQUOTE*qcontent DQUOTE 
		// 非空白控制=%d1-8/%d11/%d12/%d14-31/%d127 
		// qcontent=qtext/Quoted pair 
		// qtext=非空白控制/
		// /%d33/%d35-91/%d93-126 
		// 
		// （以“obs-”开头的名称是RFC 2822，
		// 第4节中过时的语法。16年过去了，我们不再接受。）
		in = in[1:]
	QuotedString:
		for {
			if len(in) == 0 {
				return mailbox, false
			}
			c := in[0]
			in = in[1:]

			switch {
			case c == '"':
				break QuotedString

			case c == '\\':
				// 引用对
				if len(in) == 0 {
					return mailbox, false
				}
				if in[0] == 11 ||
					in[0] == 12 ||
					(1 <= in[0] && in[0] <= 9) ||
					(14 <= in[0] && in[0] <= 127) {
					localPartBytes = append(localPartBytes, in[0])
					in = in[1:]
				} else {
					return mailbox, false
				}

			case c == 11 ||
				c == 12 ||
				// 基于
				// BNF，不允许使用空格（字符32），但RFC 3696给出了一个例子，
				// 假设它是。一些“已证实”的
				// 勘误表仍在争论这一点。我们选择接受它。
				c == 32 ||
				c == 33 ||
				c == 127 ||
				(1 <= c && c <= 8) ||
				(14 <= c && c <= 31) ||
				(35 <= c && c <= 91) ||
				(93 <= c && c <= 126):
				// qtext 
				localPartBytes = append(localPartBytes, c)

			default:
				return mailbox, false
			}
		}
	} else {
		// Atom（“.”Atom）*
	NextChar:
		for len(in) > 0 {
			// RFC 2822第3.2.4节的文本
			c := in[0]

			switch {
			case c == '\\':
				// RFC 3696中给出的示例表明，
				// 转义字符可以出现在带引号的字符串之外。几个“已验证”的勘误表
				// 继续争论这一点。我们选择接受它。
				in = in[1:]
				if len(in) == 0 {
					return mailbox, false
				}
				fallthrough

			case ('0' <= c && c <= '9') ||
				('a' <= c && c <= 'z') ||
				('A' <= c && c <= 'Z') ||
				c == '!' || c == '#' || c == '$' || c == '%' ||
				c == '&' || c == '\'' || c == '*' || c == '+' ||
				c == '-' || c == '/' || c == '=' || c == '?' ||
				c == '^' || c == '_' || c == '`' || c == '{' ||
				c == '|' || c == '}' || c == '~' || c == '.':
				localPartBytes = append(localPartBytes, in[0])
				in = in[1:]

			default:
				break NextChar
			}
		}

		if len(localPartBytes) == 0 {
			return mailbox, false
		}

		// RFC 3696第3节：
		// “期间（.”）也可能出现，但不能用于启动
		// 或结束本地部分，也不能出现两个或多个连续的
		// 句点。”
		twoDots := []byte{'.', '.'}
		if localPartBytes[0] == '.' ||
			localPartBytes[len(localPartBytes)-1] == '.' ||
			bytes.Contains(localPartBytes, twoDots) {
			return mailbox, false
		}
	}

	if len(in) == 0 || in[0] != '@' {
		return mailbox, false
	}
	in = in[1:]

	// RFC是一种域格式，但这在实践中被违反了，所以我们接受在“@”之后的任何内容都是
	// 域部分。
	if _, ok := domainToReverseLabels(in); !ok {
		return mailbox, false
	}

	mailbox.local = string(localPartBytes)
	mailbox.domain = in
	return mailbox, true
}

// domainToReverseLabels转换像foo这样的文本域名。实例com to 
// 标签列表的顺序相反，例如[com]、[example]、[foo]。
func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
	for len(domain) > 0 {
		if i := strings.LastIndexByte(domain, '.'); i == -1 {
			reverseLabels = append(reverseLabels, domain)
			domain = ""
		} else {
			reverseLabels = append(reverseLabels, domain[i+1:])
			domain = domain[:i]
		}
	}

	if len(reverseLabels) > 0 && len(reverseLabels[0]) == 0 {
		// 末尾的空标签表示绝对值。
		return nil, false
	}

	for _, label := range reverseLabels {
		if len(label) == 0 {
			// 否则空标签无效。
			return nil, false
		}

		for _, c := range label {
			if c < 33 || c > 126 {
				// 无效字符。
				return nil, false
			}
		}
	}

	return reverseLabels, true
}

func matchEmailConstraint(mailbox rfc2821Mailbox, constraint string) (bool, error) {
	// 如果约束包含@，那么它将指定一个确切的邮箱
	// 名称。
	if strings.Contains(constraint, "@") {
		constraintMailbox, ok := parseRFC2821Mailbox(constraint)
		if !ok {
			return false, fmt.Errorf("x509: internal error: cannot parse constraint %q", constraint)
		}
		return mailbox.local == constraintMailbox.local && strings.EqualFold(mailbox.domain, constraintMailbox.domain), nil
	}

	// 否则该约束类似于邮箱的域部分
	// 的DNS约束。
	return matchDomainConstraint(mailbox.domain, constraint)
}

func matchURIConstraint(uri *url.URL, constraint string) (bool, error) {
	// 来自RFC 5280，第4.2.1.10节：
	// “不包括授权
	// 组件的统一资源标识符，主机名指定为完全限定域
	// 名称（例如，如果URI不包含授权
	// 组件，或包含主机名
	// 指定为IP地址的授权组件，则应用程序必须拒绝
	// 证书。”

	host := uri.Host
	if len(host) == 0 {
		return false, fmt.Errorf("URI with empty host (%q) cannot be matched against constraints", uri.String())
	}

	if strings.Contains(host, ":") && !strings.HasSuffix(host, "]") {
		var err error
		host, _, err = net.SplitHostPort(uri.Host)
		if err != nil {
			return false, err
		}
	}

	if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") ||
		net.ParseIP(host) != nil {
		return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String())
	}

	return matchDomainConstraint(host, constraint)
}

func matchIPConstraint(ip net.IP, constraint *net.IPNet) (bool, error) {
	if len(ip) != len(constraint.IP) {
		return false, nil
	}

	for i := range ip {
		if mask := constraint.Mask[i]; ip[i]&mask != constraint.IP[i]&mask {
			return false, nil
		}
	}

	return true, nil
}

func matchDomainConstraint(domain, constraint string) (bool, error) {
	// 未指定零长度约束的含义，但此
	// 代码遵循NSS，并将其视为匹配所有内容。
	if len(constraint) == 0 {
		return true, nil
	}

	domainLabels, ok := domainToReverseLabels(domain)
	if !ok {
		return false, fmt.Errorf("x509: internal error: cannot parse domain %q", domain)
	}

	// RFC 5280表示域名中的前导句点意味着在
	// 中必须至少有一个标签，但仅限于URI和电子邮件
	// 约束，而不是DNS约束。该代码还支持针对DNS约束的
	// 行为。

	mustHaveSubdomains := false
	if constraint[0] == '.' {
		mustHaveSubdomains = true
		constraint = constraint[1:]
	}

	constraintLabels, ok := domainToReverseLabels(constraint)
	if !ok {
		return false, fmt.Errorf("x509: internal error: cannot parse domain %q", constraint)
	}

	if len(domainLabels) < len(constraintLabels) ||
		(mustHaveSubdomains && len(domainLabels) == len(constraintLabels)) {
		return false, nil
	}

	for i, constraintLabel := range constraintLabels {
		if !strings.EqualFold(constraintLabel, domainLabels[i]) {
			return false, nil
		}
	}

	return true, nil
}

// checkNameConstraints检查c是否允许子证书声明类型为nameType的
// 给定名称。参数parsedName包含解析后的
// 形式的名称，适合传递给match函数。在给定的计数中跟踪
// 的比较总数，并且不应超过给定的
// 限制。
func (c *Certificate) checkNameConstraints(count *int,
	maxConstraintComparisons int,
	nameType string,
	name string,
	parsedName any,
	match func(parsedName, constraint any) (match bool, err error),
	permitted, excluded any) error {

	excludedValue := reflect.ValueOf(excluded)

	*count += excludedValue.Len()
	if *count > maxConstraintComparisons {
		return CertificateInvalidError{c, TooManyConstraints, ""}
	}

	for i := 0; i < excludedValue.Len(); i++ {
		constraint := excludedValue.Index(i).Interface()
		match, err := match(parsedName, constraint)
		if err != nil {
			return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
		}

		if match {
			return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is excluded by constraint %q", nameType, name, constraint)}
		}
	}

	permittedValue := reflect.ValueOf(permitted)

	*count += permittedValue.Len()
	if *count > maxConstraintComparisons {
		return CertificateInvalidError{c, TooManyConstraints, ""}
	}

	ok := true
	for i := 0; i < permittedValue.Len(); i++ {
		constraint := permittedValue.Index(i).Interface()

		var err error
		if ok, err = match(parsedName, constraint); err != nil {
			return CertificateInvalidError{c, CANotAuthorizedForThisName, err.Error()}
		}

		if ok {
			break
		}
	}

	if !ok {
		return CertificateInvalidError{c, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is not permitted by any constraint", nameType, name)}
	}

	return nil
}

// isValid在c上执行有效性检查，因为它是将
// 附加到currentChain中的链的候选对象。
func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
	if len(c.UnhandledCriticalExtensions) > 0 {
		return UnhandledCriticalExtension{}
	}

	if len(currentChain) > 0 {
		child := currentChain[len(currentChain)-1]
		if !bytes.Equal(child.RawIssuer, c.RawSubject) {
			return CertificateInvalidError{c, NameMismatch, ""}
		}
	}

	now := opts.CurrentTime
	if now.IsZero() {
		now = time.Now()
	}
	if now.Before(c.NotBefore) {
		return CertificateInvalidError{
			Cert:   c,
			Reason: Expired,
			Detail: fmt.Sprintf("current time %s is before %s", now.Format(time.RFC3339), c.NotBefore.Format(time.RFC3339)),
		}
	} else if now.After(c.NotAfter) {
		return CertificateInvalidError{
			Cert:   c,
			Reason: Expired,
			Detail: fmt.Sprintf("current time %s is after %s", now.Format(time.RFC3339), c.NotAfter.Format(time.RFC3339)),
		}
	}

	maxConstraintComparisons := opts.MaxConstraintComparisions
	if maxConstraintComparisons == 0 {
		maxConstraintComparisons = 250000
	}
	comparisonCount := 0

	var leaf *Certificate
	if certType == intermediateCertificate || certType == rootCertificate {
		if len(currentChain) == 0 {
			return errors.New("x509: internal error: empty chain when appending CA cert")
		}
		leaf = currentChain[0]
	}

	if (certType == intermediateCertificate || certType == rootCertificate) &&
		c.hasNameConstraints() && leaf.hasSANExtension() {
		err := forEachSAN(leaf.getSANExtension(), func(tag int, data []byte) error {
			switch tag {
			case nameTypeEmail:
				name := string(data)
				mailbox, ok := parseRFC2821Mailbox(name)
				if !ok {
					return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox)
				}

				if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
					func(parsedName, constraint any) (bool, error) {
						return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string))
					}, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
					return err
				}

			case nameTypeDNS:
				name := string(data)
				if _, ok := domainToReverseLabels(name); !ok {
					return fmt.Errorf("x509: cannot parse dnsName %q", name)
				}

				if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
					func(parsedName, constraint any) (bool, error) {
						return matchDomainConstraint(parsedName.(string), constraint.(string))
					}, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
					return err
				}

			case nameTypeURI:
				name := string(data)
				uri, err := url.Parse(name)
				if err != nil {
					return fmt.Errorf("x509: internal error: URI SAN %q failed to parse", name)
				}

				if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
					func(parsedName, constraint any) (bool, error) {
						return matchURIConstraint(parsedName.(*url.URL), constraint.(string))
					}, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
					return err
				}

			case nameTypeIP:
				ip := net.IP(data)
				if l := len(ip); l != net.IPv4len && l != net.IPv6len {
					return fmt.Errorf("x509: internal error: IP SAN %x failed to parse", data)
				}

				if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip,
					func(parsedName, constraint any) (bool, error) {
						return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet))
					}, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil {
					return err
				}

			default:
				// 忽略未知的SAN类型。
			}

			return nil
		})

		if err != nil {
			return err
		}
	}

	// 忽略密钥使用状态标志。来自工程安全部的彼得·
	// 古特曼：一个欧洲政府CA将其签名证书标记为
	// 仅对加密有效，但没有人注意到。另一家
	// 欧洲CA将其签名密钥标记为对
	// 签名无效。另一个CA将其自己的受信任根证书
	// 标记为对证书签名无效。另一个国家CA 
	// 发布了一份证书，用于加密
	// 国家税务机关的数据，该证书被标记为仅可用于
	// 数字签名，但不可用于加密。然而，另一个CA颠倒了
	// 由于对
	// 编码endianness，本质上是在
	// 证书中设置一个随机密钥用法。另一个CA创建了一个自失效的
	// 证书，添加了一个证书策略声明，规定
	// 必须严格按照
	// 密钥用法中的规定使用该证书，以及一个包含标志的密钥用法，该标志指示RSA 
	// 加密密钥只能用于Diffie-Hellman密钥协议。

	if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) {
		return CertificateInvalidError{c, NotAuthorizedToSign, ""}
	}

	if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
		numIntermediates := len(currentChain) - 1
		if numIntermediates > c.MaxPathLen {
			return CertificateInvalidError{c, TooManyIntermediates, ""}
		}
	}

	return nil
}

// 验证尝试通过在opts中构建从c到
// 证书的一个或多个链来验证c。根，在opts中使用证书。如果需要
// 中间体。如果成功，它将返回一个或多个链，其中链的第一个
// 元素是c，最后一个元素是opts。根。
// 
// 如果选择。root为nil，可能会使用平台验证器，
// 验证细节可能与下面描述的不同。如果系统
// 根不可用，则返回的错误类型为SystemRootsError。
// 
// 中间体中的名称约束将应用于链中所有声称的名称
// 而不仅仅是OPT。德斯纳姆。因此，叶子声明
// 示例无效。如果中间人不允许，即使是例子。com不是
// 正在验证的名称。请注意，不支持
// 目录名约束。
// 
// 名称约束验证遵循RFC 5280中的规则，并添加了
// DNS名称约束可以使用为电子邮件和URI定义的前导句点格式
// 。当约束的前导句点为
// 时，表示必须在
// 约束名称前至少加上一个标签才能被视为有效。
// 
// 扩展密钥使用值强制嵌套在一个链中，因此枚举EKUs的中间
// 或根阻止叶断言不在该
// 列表中的EKU。（虽然没有具体说明，但通常的做法是限制
// CA可以颁发的证书类型。）
// 
// 不支持使用SHA1 with RSA和ECDSAW且带有SHA1签名的证书，
// 并且不会用于构建链。
// 
// 警告：此函数不执行任何吊销检查。
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
	// 特定于平台的验证需要ASN。1内容
	// 这使得行为在不同平台上保持一致。
	if len(c.Raw) == 0 {
		return nil, errNotParsed
	}
	for i := 0; i < opts.Intermediates.len(); i++ {
		c, err := opts.Intermediates.cert(i)
		if err != nil {
			return nil, fmt.Errorf("crypto/x509: error fetching intermediate: %w", err)
		}
		if len(c.Raw) == 0 {
			return nil, errNotParsed
		}
	}

	// 如果根来自SystemCertPool，则在可用的情况下使用平台验证程序。
	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
		if opts.Roots == nil {
			return c.systemVerify(&opts)
		}
		if opts.Roots != nil && opts.Roots.systemPool {
			platformChains, err := c.systemVerify(&opts)
			// 如果平台验证程序成功，或者没有其他
			// 根，则返回平台验证程序结果。否则，请使用Go验证程序继续
			// 。
			if err == nil || opts.Roots.len() == 0 {
				return platformChains, err
			}
		}
	}

	if opts.Roots == nil {
		opts.Roots = systemRootsPool()
		if opts.Roots == nil {
			return nil, SystemRootsError{systemRootsErr}
		}
	}

	err = c.isValid(leafCertificate, nil, &opts)
	if err != nil {
		return
	}

	if len(opts.DNSName) > 0 {
		err = c.VerifyHostname(opts.DNSName)
		if err != nil {
			return
		}
	}

	var candidateChains [][]*Certificate
	if opts.Roots.contains(c) {
		candidateChains = append(candidateChains, []*Certificate{c})
	} else {
		if candidateChains, err = c.buildChains(nil, []*Certificate{c}, nil, &opts); err != nil {
			return nil, err
		}
	}

	keyUsages := opts.KeyUsages
	if len(keyUsages) == 0 {
		keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
	}

	// 如果任何密钥使用都可以接受，那么我们就完成了。
	for _, usage := range keyUsages {
		if usage == ExtKeyUsageAny {
			return candidateChains, nil
		}
	}

	for _, candidate := range candidateChains {
		if checkChainForKeyUsage(candidate, keyUsages) {
			chains = append(chains, candidate)
		}
	}

	if len(chains) == 0 {
		return nil, CertificateInvalidError{c, IncompatibleUsage, ""}
	}

	return chains, nil
}

func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
	n := make([]*Certificate, len(chain)+1)
	copy(n, chain)
	n[len(chain)] = cert
	return n
}

// maxChainSignatureChecks是调用buildChains将（以传递方式）进行的CheckSignatureFrom调用
// 的最大数量。大多数链的
// 长度小于15个证书，因此这为多个链留出了空间，而
// 则为因不同中间产物具有相同主题而导致的检查失败留出了空间。
const maxChainSignatureChecks = 100

func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) {
	var (
		hintErr  error
		hintCert *Certificate
	)

	considerCandidate := func(certType int, candidate *Certificate) {
		for _, cert := range currentChain {
			if cert.Equal(candidate) {
				return
			}
		}

		if sigChecks == nil {
			sigChecks = new(int)
		}
		*sigChecks++
		if *sigChecks > maxChainSignatureChecks {
			err = errors.New("x509: signature check attempts limit reached while verifying certificate chain")
			return
		}

		if err := c.CheckSignatureFrom(candidate); err != nil {
			if hintErr == nil {
				hintErr = err
				hintCert = candidate
			}
			return
		}

		err = candidate.isValid(certType, currentChain, opts)
		if err != nil {
			return
		}

		switch certType {
		case rootCertificate:
			chains = append(chains, appendToFreshChain(currentChain, candidate))
		case intermediateCertificate:
			if cache == nil {
				cache = make(map[*Certificate][][]*Certificate)
			}
			childChains, ok := cache[candidate]
			if !ok {
				childChains, err = candidate.buildChains(cache, appendToFreshChain(currentChain, candidate), sigChecks, opts)
				cache[candidate] = childChains
			}
			chains = append(chains, childChains...)
		}
	}

	for _, root := range opts.Roots.findPotentialParents(c) {
		considerCandidate(rootCertificate, root)
	}
	for _, intermediate := range opts.Intermediates.findPotentialParents(c) {
		considerCandidate(intermediateCertificate, intermediate)
	}

	if len(chains) > 0 {
		err = nil
	}
	if len(chains) == 0 && err == nil {
		err = UnknownAuthorityError{c, hintErr, hintCert}
	}

	return
}

func validHostnamePattern(host string) bool { return validHostname(host, true) }
func validHostnameInput(host string) bool   { return validHostname(host, false) }

// validHostname报告主机是否是可匹配的有效主机名，或根据RFC 6125 2.2进行匹配的
// ，并对
// 遗留值进行了一些宽容处理。
func validHostname(host string, isPattern bool) bool {
	if !isPattern {
		host = strings.TrimSuffix(host, ".")
	}
	if len(host) == 0 {
		return false
	}

	for i, part := range strings.Split(host, ".") {
		if part == "" {
			// 空标签。
			return false
		}
		if isPattern && i == 0 && part == "*" {
			// 只允许最左端的通配符，因为这些通配符是唯一匹配的
			// 而匹配的文本'*'字符可能永远不会是预期的行为。
			continue
		}
		for j, c := range part {
			if 'a' <= c && c <= 'z' {
				continue
			}
			if '0' <= c && c <= '9' {
				continue
			}
			if 'A' <= c && c <= 'Z' {
				continue
			}
			if c == '-' && j != 0 {
				continue
			}
			if c == '_' {
				// 在主机名中不是有效字符，但通常在WebPKI之外的部署中可以找到
				// 。
				continue
			}
			return false
		}
	}

	return true
}

func matchExactly(hostA, hostB string) bool {
	if hostA == "" || hostA == "." || hostB == "" || hostB == "." {
		return false
	}
	return toLowerCaseASCII(hostA) == toLowerCaseASCII(hostB)
}

func matchHostnames(pattern, host string) bool {
	pattern = toLowerCaseASCII(pattern)
	host = toLowerCaseASCII(strings.TrimSuffix(host, "."))

	if len(pattern) == 0 || len(host) == 0 {
		return false
	}

	patternParts := strings.Split(pattern, ".")
	hostParts := strings.Split(host, ".")

	if len(patternParts) != len(hostParts) {
		return false
	}

	for i, patternPart := range patternParts {
		if i == 0 && patternPart == "*" {
			continue
		}
		if patternPart != hostParts[i] {
			return false
		}
	}

	return true
}

// toLowerCaseASCII返回in的小写版本。见RFC 6125 6.4.1。我们使用
// 一个显式ASCII函数，以避免在DNS标签上执行Unicode操作时出现任何尖角。
func toLowerCaseASCII(in string) string {
	// 如果字符串已经是小写，那么就没什么可做的了。
	isAlreadyLowerCase := true
	for _, c := range in {
		if c == utf8.RuneError {
			// 如果我们得到一个UTF-8错误，那么在无效序列中可能有
			// 大写ASCII字节。
			isAlreadyLowerCase = false
			break
		}
		if 'A' <= c && c <= 'Z' {
			isAlreadyLowerCase = false
			break
		}
	}

	if isAlreadyLowerCase {
		return in
	}

	out := []byte(in)
	for i, c := range out {
		if 'A' <= c && c <= 'Z' {
			out[i] += 'a' - 'A'
		}
	}
	return string(out)
}

// 如果c是指定主机的有效证书，则VerifyHostname返回nil。
// 否则返回描述不匹配的错误。
// 
// IP地址可以选择用方括号括起来，并被选中
// 针对IPAddresses字段。其他名称在DNSNames字段中以不区分大小写的
// 进行检查。如果名称是有效的主机名，则证书
// 字段的最左边可以有一个通配符。
// 
// 请注意，旧版通用名字段被忽略。
func (c *Certificate) VerifyHostname(h string) error {
	// IP地址可以用[]写。
	candidateIP := h
	if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
		candidateIP = h[1 : len(h)-1]
	}
	if ip := net.ParseIP(candidateIP); ip != nil {
		// 我们只将IP地址与IP SAN匹配。
		// 参见RFC 6125附录B.2。
		for _, candidate := range c.IPAddresses {
			if ip.Equal(candidate) {
				return nil
			}
		}
		return HostnameError{c, candidateIP}
	}

	candidateName := toLowerCaseASCII(h) // 在循环内保存分配。
	validCandidateName := validHostnameInput(candidateName)

	for _, match := range c.DNSNames {
		// 理想情况下，我们只能根据RFC 6125匹配有效的主机名，就像
		// 浏览器（或多或少）一样，但实际上Go用于更广泛的
		// 上下文数组，甚至不能假设DNS解析。相反，
		// 始终允许完美匹配，并且只对有效主机名应用通配符和尾随
		// 点处理。
		if validCandidateName && validHostnamePattern(match) {
			if matchHostnames(match, candidateName) {
				return nil
			}
		} else {
			if matchExactly(match, candidateName) {
				return nil
			}
		}
	}

	return HostnameError{c, h}
}

func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
	usages := make([]ExtKeyUsage, len(keyUsages))
	copy(usages, keyUsages)

	if len(chain) == 0 {
		return false
	}

	usagesRemaining := len(usages)

	// 我们沿着列表走下去，划掉每个证书不支持的任何用法。如果我们去掉所有用法，那么
	// 链是不可接受的。

NextCert:
	for i := len(chain) - 1; i >= 0; i-- {
		cert := chain[i]
		if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
			// 证书未指定任何扩展密钥用法。
			continue
		}

		for _, usage := range cert.ExtKeyUsage {
			if usage == ExtKeyUsageAny {
				// 该证书明确适用于任何用途。
				continue NextCert
			}
		}

		const invalidUsage ExtKeyUsage = -1

	NextRequestedUsage:
		for i, requestedUsage := range usages {
			if requestedUsage == invalidUsage {
				continue
			}

			for _, usage := range cert.ExtKeyUsage {
				if requestedUsage == usage {
					continue NextRequestedUsage
				}
			}

			usages[i] = invalidUsage
			usagesRemaining--
			if usagesRemaining == 0 {
				return false
			}
		}
	}

	return true
}
